package model.common.namespace;

import exception.InconsistentModelException;
import model.common.root.Element;

/** A NamedElement is an Element in a model that may have a name. The name may be used for identification of the NamedElement
within Namespaces where its name is accessible.<p>
<b>NOTE</b>. The name of a NamedElement is optional, which provides for the possibility of the absence of a name (which is different
from the empty name).<p>
NamedElements may appear within a Namespace according to rules that specify how one NamedElement is distinguishable from
another. The default rule is that two members are distinguishable if they have different names or if they have the same names , but
their metaclasses are different and neither is a (direct or indirect) subclass of the other. This rule may be overridden for particular
cases, such as Operations that are distinguished by their signature (see sub clause 9.6).<p>
The visibility of a NamedElement provides a means to constrain the usage of the Element, either in Namespaces or in access to
the Element. It is intended for use in conjunction with import, generalization, and access mechanisms.<p>
A NamedElement may, in addition to having an explicit name , be associated with a StringExpression (see sub clause 8.3) that may
be used to specify a calculated name for the NamedElement. In a template (see sub clause 7.3), a NamedElement may have an
associated StringExpression whose subexpressions may be ParameteredElements exposed by TemplateParameters. When the
template is bound, the exposed subexpressions are substituted with the actuals substituted for the TemplateParameters. The value
of the StringExpression is then a string resulting from concatenating the values of the subexpression, which then provides the
name of the NamedElement.<p>
A NamedElement may have both a name and a nameExpression associated with it. In this case, the name can be used as an alias for
the NamedElement, which may be used, for example, in referencing the element in a Constraint expression. (This avoids the need
to use StringExpressions in textual surface notation, which is often cumbersome, although it does not preclude it.)
*/
public abstract class NamedElement extends Element {
	
	// Attributes
	private static final String DEFAULT_SEPARATOR = "::";
	
	private String separator;
	
	private String name;
	
	private VisibilityKind visibility;
	
	/** Path to name without separator at the end. */
	private String pathName;
	
	// Constructors
	/**
	 * @param name
	 * @param pathName
	 * @param visibility
	 */
	public NamedElement(String name, String pathName, VisibilityKind visibility) {
		this.name = name;
		this.pathName = pathName;
		this.visibility = visibility;
		this.separator = DEFAULT_SEPARATOR;
	}
	
	/**
	 * @param name
	 * @param pathName
	 * @param visibility
	 * @param separator
	 */
	public NamedElement(String name, String pathName, VisibilityKind visibility, String separator) {
		this.name = name;
		this.pathName = pathName;
		this.visibility = visibility;
		this.separator = separator;
	}

	/**
	 * @param qualifiedName = pathName + separator + name
	 * @param visibility
	 * @param separator
	 */
	public NamedElement(String qualifiedName, VisibilityKind visibility, String separator) {
		this.separator = separator;
		int lastIndexOfSeparator = qualifiedName.lastIndexOf(separator);
		if (lastIndexOfSeparator == -1) {
			this.name = qualifiedName;
			this.pathName = "";
		} else {
			this.name = qualifiedName.substring(
					lastIndexOfSeparator + separator.length(), qualifiedName.length());
			this.pathName = qualifiedName.substring(0, lastIndexOfSeparator);
		}
	}

	// Methods
	/**
	 * @return A String with pathName + separator + name
	 */
	public String getQualifiedName() {
		return new String(pathName + separator + name);
	}
	
	public String getName() {
		return this.name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public VisibilityKind getVisibility() {
		return visibility;
	}

	public void setVisibility(VisibilityKind visibility) {
		this.visibility = visibility;
	}

	public String getPathName() {
		return this.pathName;
	}
	
	public void setPathName(String pathName) {
		this.pathName = pathName;
	}

}
